package ByteTree;

import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.List;

import Verifier.GeneralUtil;
/**
 * 
 * @author Hadar Miller
 *
 */
public class Node {

	protected List<Node> children;
	protected int numChildren;
	
	/**
	 * 
	 * @param numChildren number of children of the node.
	 * @param data the byte array representing all the node's children.
	 * @throws Exception when the byte array does not representing a correct node.
	 */
	public Node(int numChildren, byte [] data) throws Exception {
		
		this.numChildren = numChildren;
		this.children = new ArrayList<Node>();
		
		int counter = numChildren;
		ByteArrayOutputStream byteArrayStream = new ByteArrayOutputStream();
		int num;
		int sizeNode;
		while (counter > 0) {
			try
			{
				byteArrayStream.write(data, 1, 4);
				/*holds the number of children or the number of bytes of the data*/
				num = GeneralUtil.byteArrayToInt(byteArrayStream.toByteArray());
				byteArrayStream.reset();
				/* create new leaf */
				if (data[0] == 0x01) {
					byteArrayStream.write(data, 5, num);
					this.children.add(new Leaf(num, byteArrayStream.toByteArray()));
					byteArrayStream.reset();
					byteArrayStream.write(data, num + 5, data.length - (num + 5));
					data = byteArrayStream.toByteArray();
					byteArrayStream.reset();
				}
				
				/* create new node */
				else if (data[0] == 0x00) {
					byteArrayStream.write(data, 5, data.length - 5);
					this.children.add(new Node(num, byteArrayStream.toByteArray()));
					byteArrayStream.reset();
					sizeNode = computeSizeNode(data, num);
					byteArrayStream.write(data, sizeNode, data.length - sizeNode);
					data = byteArrayStream.toByteArray();
					byteArrayStream.reset();
				}
			}
			catch (Exception e) {
				throw new Exception("a problem occured while trying to add child number " + (this.numChildren - counter + 1) + " to the node\n" + e.getMessage());
			}
			counter--;
		}
		
		
	}
	/* compute the size of a node in bytes */
	private static int computeSizeNode(byte [] nodeData, int numOfChildren) throws Exception {
		
		ByteArrayOutputStream byteArrayStream = new ByteArrayOutputStream();
		int size = 5;
		int num; 
		int temp;
		try {
			byteArrayStream.write(nodeData, 5, nodeData.length - 5);
			nodeData = byteArrayStream.toByteArray();
			byteArrayStream.reset();
			
			
			
			for (int i = 0; i < numOfChildren; i++)
			{
				byteArrayStream.write(nodeData, 1, 4);
				num = GeneralUtil.byteArrayToInt(byteArrayStream.toByteArray());
				byteArrayStream.reset();
				
				if (nodeData[0] == 0x01) {
					size += 5 + num;
					byteArrayStream.write(nodeData, num + 5, nodeData.length - (num + 5));
					nodeData = byteArrayStream.toByteArray();
					byteArrayStream.reset();
				}
				else if (nodeData[0] == 0x00) {
					temp = computeSizeNode(nodeData, num);
					size += temp;
					byteArrayStream.write(nodeData, temp, nodeData.length - temp);
					nodeData = byteArrayStream.toByteArray();
					byteArrayStream.reset();
				}			
			}
		} catch (Exception e) {
			throw new Exception("cannot compute size of node\n" + e.getMessage()); 
		}
		
		return size;		
	}
	
	/**
	 * 
	 * @return the number of children of the node.
	 */
	public int getNumChildren() {
		return numChildren;
	}
	
	/**
	 * 
	 * @return list of the children nodes. 
	 */
	public List<Node> getChildren() {
		return children;
	}
	
	/**
	 * 
	 * @return true if the node is a leaf and false otherwise.
	 */
	public boolean isLeaf() {
		return false;
	}
	
	
	/**
	 * converts the node into its byte array representation.
	 * @return byte array representation of the node.
	 */
	public byte[] toByteArray() throws Exception
	{
		ByteArrayOutputStream byteArrayStream = new ByteArrayOutputStream();
		try{
			byteArrayStream.write((byte)0x00);
			byteArrayStream.write(GeneralUtil.intToByteArray(numChildren));
			for (Node n : children)
			{
				if (n.isLeaf())
					byteArrayStream.write(((Leaf)n).toByteArray());
				else
					byteArrayStream.write(n.toByteArray());
			}
		} catch (Exception e) {
			throw new Exception("converting node to byte array faild\n" + e.getMessage());
		}
		return byteArrayStream.toByteArray();
	}
}
